﻿using Elastic.Channels;
using Elastic.CommonSchema.Serilog;
using Elastic.Ingest.Elasticsearch;
using Elastic.Ingest.Elasticsearch.DataStreams;
using Elastic.Serilog.Sinks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using Serilog.Core;
using Serilog.Enrichers.Sensitive;
using Serilog.Events;
using Shared.Service.Logging.Serilog;
using Shared.Service.Logging.Serilog.Configurations;
using System;
using System.Collections.Generic;

namespace Shared.Service.Logging
{
    public static class SerilogExtension
    {
        public static void AddSerilog(this IServiceCollection services, IConfiguration configuration, string dataName)
        {
            var logSetting = configuration.GetSection(nameof(SerilogSetting)).Get<SerilogSetting>();
            if (logSetting == null)
                throw new Exception("Serilog setting can not be null");

            var logLevelString = logSetting.LogLevel;

            if (!TryParseLogLevel(logLevelString, out var logLevel))
            {
                logLevel = LogEventLevel.Information;
            }

            var levelSwitch = new LoggingLevelSwitch(logLevel);
            services.AddSingleton(levelSwitch);

            var loggerConfiguration = new LoggerConfiguration()
#if DEBUG
                .MinimumLevel.Debug()
#else
                .MinimumLevel.Information()
#endif
                .MinimumLevel.Override("Microsoft", levelSwitch)
                .MinimumLevel.Override("Microsoft.EntityFrameworkCore", levelSwitch)
                .MinimumLevel.Override("Volo.Abp", levelSwitch)
                .Enrich.WithProperty("Application", dataName)
                .WriteTo.Console()
                .Enrich.FromLogContext();

            //loggerConfiguration = logSetting.LogType switch
            //{
            //    LogType.LocalFile => BuildFileLogConfig(loggerConfiguration, logSetting, levelSwitch, appName),
            //    LogType.Elasticsearch => BuildEsLogConfig(loggerConfiguration, logSetting, levelSwitch, appName),
            //    _ => loggerConfiguration
            //};

            //上游ip
            loggerConfiguration.Enrich.WithRequestHeader("x-envoy-external-address");
            //remoteip 浏览器侧ip 该header头是网关透传的
            loggerConfiguration.Enrich.WithRequestHeader("remoteip");

            //x-forwarded-for
            loggerConfiguration.Enrich.WithRequestHeader("x-forwarded-for");

            //手机、身份证敏感信息日志加密
            loggerConfiguration.Enrich.WithSensitiveDataMasking(
                            options =>
                            {
                                options.MaskingOperators = new List<IMaskingOperator>
                                {
                                    new LogMobileMasking(),
                                    new LogIdCardMasking()
                                    // etc etc
                                };
                            });
            var currentEnv = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");

            //  var dataName = Assembly.GetExecutingAssembly()?.GetName()?.Name ?? "demo";

            Log.Logger = loggerConfiguration.WriteTo.Elasticsearch(new[] { new Uri(configuration["ElasticSearch:Url"]) }, opts =>
                {
                    opts.DataStream = new DataStreamName($"{dataName}", currentEnv, DateTime.Now.ToString("yyyy-MM-dd"));
                    opts.BootstrapMethod = BootstrapMethod.Failure;

                    opts.TextFormatting = new EcsTextFormatterConfiguration
                    {
                        MapCustom = (e, _) => e
                    };
                    opts.ConfigureChannel = channelOpts =>
                    {
                        channelOpts.BufferOptions = new BufferOptions
                        {
                            ExportMaxConcurrency = 10
                        };
                    };
                }).CreateLogger();
        }

        private static bool TryParseLogLevel(string logLevel, out LogEventLevel level)
        {
            if (string.IsNullOrWhiteSpace(logLevel))
            {
                level = LogEventLevel.Verbose;
                return false;
            }

            try
            {
                level = (LogEventLevel)Enum.Parse(typeof(LogEventLevel), logLevel);
                return true;
            }
            catch
            {
                level = LogEventLevel.Verbose;
            }

            return false;
        }
    }

}